package com.test.utils.file;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class FilesUtils {

    /**
     * 文本文件写入操作
     * @param txtFilePath   文本文件路径
     * @param data          待写入的内容（string）
     * @param charset       编码类型
     * @param isAppend      是否是追加模式
     * @throws IOException  文件不存在抛出次错误
     */
    public static void fileTxtWrite(String txtFilePath,String data,String charset,boolean isAppend) throws IOException {
        FileUtils.write(new File(txtFilePath) ,data ,charset ,isAppend );
    }

    /**
     * 文本文件写入操作
     * @param txtFilePath   文本文件路径
     * @param data          待写入的内容（List）
     * @param charset       编码类型
     * @param isAppend      是否是追加模式
     * @throws IOException  文件不存在抛出次错误
     */
    public static void fileTxtWrite(String txtFilePath,List<String> data,String charset,boolean isAppend) throws IOException {
        FileUtils.writeLines(new File(txtFilePath), data, charset, isAppend);
    }

    /**
     * 文本文件写入操作
     * @param txtFilePath   文本文件路径
     * @param data          待写入的内容（array）
     * @param charset       编码类型
     * @param isAppend      是否是追加模式
     * @throws IOException  文件不存在抛出次错误
     */
    public static void fileTxtWrite(String txtFilePath,String[] data,String charset,boolean isAppend) throws IOException {
        fileTxtWrite(txtFilePath ,Arrays.asList(data) ,charset ,isAppend ) ;
    }


    /**
     * 文本文件读操作  返回多条文本
     * @param txtFilePath   文本文件路径
     * @param charset       编码类型
     * @return List<String> 返回多条文本
     * @throws IOException  文件不存在抛出次错误
     */
    public static List<String> fileTxtReadLines(String txtFilePath,String charset) throws IOException {
        return FileUtils.readLines(new File(txtFilePath), charset);
    }

    /**
     * 文本文件读操作  返回一条文本
     * @param txtFilePath   文本文件路径
     * @param charset       编码类型
     * @return String       返回一条文本
     * @throws IOException  文件不存在抛出次错误
     */
    public static String fileTxtReadLine(String txtFilePath,String charset) throws IOException {
        return FileUtils.readFileToString(new File(txtFilePath), charset);
    }

    /**
     * 文件或者文件夹删除
     * @param txtFilePath   文本文件路径
     * @param isForce       是否强制删除 true文件夹不是空删除也不会抛错
     * @throws IOException  文件相关错误
     */
    public void deleteDirectory(String txtFilePath,boolean isForce) throws IOException {
        if(isForce){
            FileUtils.deleteQuietly(new File(txtFilePath));
        }else{
            FileUtils.deleteDirectory(new File(txtFilePath));
        }
    }


    //移动文件 或 文件夹=====================================================================
    //static void：moveDirectory(File srcDir, File destDir)
    //FileUtils.moveDirectory(new File("D:/cxyapi1"), new File("D:/cxyapi2")); //注意这里 第二个参数文件不存在会引发异常
    //static void:moveDirectoryToDirectory(File src, File destDir, boolean createDestDir)
    //FileUtils.moveDirectoryToDirectory(new File("D:/cxyapi2"), new File("D:/cxyapi3"), true);
    /* 上面两个方法的不同是：
     * moveDirectory：D:/cxyapi2里的内容是D:/cxyapi1的内容。
     * moveDirectoryToDirectory：D:/cxyapi2文件夹移动到到D:/cxyapi3里
     *
     * 下面的3个都比较简单没提供示例，只提供了api
     * 其中moveToDirectory和其他的区别是 它能自动识别操作文件还是文件夹
     */
    //static void:moveFileToDirectory(srcFile, destDir, createDestDir)
    //static void:moveFile(File srcFile, File destFile)
    //static void:moveToDirectory(File src, File destDir, boolean createDestDir)

    //文件复制或者移动=========================================================================
    //结果是cxyapi和cxyapi1在同一目录
    //FileUtils.copyDirectory(new File("D:/cxyapi"), new File("D:/cxyapi1"));
    //结果是将cxyapi拷贝到cxyapi2下
    //FileUtils.copyDirectoryToDirectory(new File("D:/cxyapi"), new File("D:/cxyapi2"));

    //拷贝文件
    //FileUtils.copyFile(new File("d:/cxyapi.xml"), new File("d:/cxyapi.xml.bak"));
    //拷贝文件到目录中
    //FileUtils.copyFileToDirectory(new File("d:/cxyapi.xml"), new File("d:/cxyapi"));
    //拷贝url到文件
    //FileUtils.copyURLToFile(new URL("http://www.cxyapi.com/rss/cxyapi.xml"), new File("d:/cxyapi.xml"));


    //文件相关其他操作
    //判断是否包含文件或者文件夹
    //boolean b=FileUtils.directoryContains(new File("D:/cxyapi"), new File("D:/cxyapi/cxyapi.txt"));
    //System.out.println(b);

    //获得临时目录 和 用户目录
    //System.out.println(FileUtils.getTempDirectoryPath());
    //System.out.println(FileUtils.getUserDirectoryPath());

    //打开流，如果不存在创建文件及其目录结构
    //第二个参数表示 文件流是否是追加方式
    //FileOutputStream fos=FileUtils.openOutputStream(new File("D:/cxyapi/cxyapi.txt"),true);
    //fos.write(new String("欢迎访问：www.cxyapi.com\r\n").getBytes());
    //fos.close();

    //文件 或 文件夹大小
    //System.out.println(FileUtils.sizeOf(new File("D:/cxyapi")));
    //System.out.println(FileUtils.sizeOfDirectory(new File("D:/cxyapi")));


    /*判断文件是否存在*/
    public static boolean isExists(String filePath) {
        File file = new File(filePath);
        return file.exists();
    }

    /*判断是否是文件夹*/
    public static boolean isDir(String path) {
        File file = new File(path);
        if(file.exists()){
            return file.isDirectory();
        }else{
            return false;
        }
    }

    /**
     * 文件或者目录重命名
     * @param oldFilePath 旧文件路径
     * @param newName 新的文件名,可以是单个文件名和绝对路径
     * @return boolean
     */
    public static boolean renameTo(String oldFilePath, String newName) {
        try {
            File oldFile = new File(oldFilePath);
            //若文件存在
            if(oldFile.exists()){
                //判断是全路径还是文件名
                if (!newName.contains("/") && !newName.contains("\\")){
                    //单文件名，判断是windows还是Linux系统
                    String absolutePath = oldFile.getAbsolutePath();
                    if(newName.indexOf("/") > 0){
                        //Linux系统
                        newName = absolutePath.substring(0, absolutePath.lastIndexOf("/") + 1)  + newName;
                    }else{
                        newName = absolutePath.substring(0, absolutePath.lastIndexOf("\\") + 1)  + newName;
                    }
                }
                File file = new File(newName);
                //判断重命名后的文件是否存在
                if(file.exists()){
                    System.out.println("该文件已存在,不能重命名");
                }else{
                    //不存在，重命名
                    return oldFile.renameTo(file);
                }
            }else {
                System.out.println("原该文件不存在,不能重命名");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /*文件拷贝操作*/
    public static void copy(String sourceFile, String targetFile) {
        File source = new File(sourceFile);
        File target = new File(targetFile);
        target.getParentFile().mkdirs();
        FileInputStream fis = null;
        FileOutputStream fos = null;
        FileChannel in = null;
        FileChannel out = null;
        try {
            fis = new FileInputStream(source);
            fos = new FileOutputStream(target);
            in = fis.getChannel();//得到对应的文件通道
            out = fos.getChannel();//得到对应的文件通道
            in.transferTo(0, in.size(), out);//连接两个通道，并且从in通道读取，然后写入out通道
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (out != null){
                    out.close();
                }
                if (in != null){
                    in.close();
                }
                if (fos != null){
                    fos.close();
                }
                if (fis != null){
                    fis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /*读取Text文件操作*/
    public static String readText(String filePath) {
        StringBuilder lines = new StringBuilder();
        try {
            FileReader fileReader = new FileReader(filePath);
            BufferedReader bufferedReader = new BufferedReader(fileReader);
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                lines.append(line).append("\n");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return lines.toString();
    }

    /*写入Text文件操作*/
    public static void writeText(String filePath, String content,boolean isAppend) {
        FileOutputStream outputStream = null;
        OutputStreamWriter outputStreamWriter = null;
        BufferedWriter bufferedWriter = null;
        try {
            outputStream = new FileOutputStream(filePath,isAppend);
            outputStreamWriter = new OutputStreamWriter(outputStream);
            bufferedWriter = new BufferedWriter(outputStreamWriter);
            bufferedWriter.write(content);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try{
                if(bufferedWriter != null){
                    bufferedWriter.close();
                }
                if (outputStreamWriter != null){
                    outputStreamWriter.close();
                }
                if (outputStream != null){
                    outputStream.close();
                }
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }

    /**
     * oss通用单张图片上传
     * @param picture 大文本对象数组
     * @param filePath 文件保存路径
     */
    /*public static Map<String,Object> upload(MultipartFile picture, String filePath) {
        Map<String,Object> responseMap = new HashMap<String,Object>();
        List<String> imageUrlList = new ArrayList<>();
        try{
            if (picture.getSize() > 0){
                String originalFilename = picture.getOriginalFilename(); //获取图片原来名称
                String filePathName = filePath + CommonUtils.uuid() + "."+ FilenameUtils.getExtension(originalFilename); //真实的图片保存相对路径
                //boolean isOK = AliOssUtils.saveFileObject(filePathName,picture.getInputStream());//将图片上传到oss服务器
                if(isOK){
                    imageUrlList.add(filePathName);
                    responseMap.ok("上传成功");
                }else{
                    return responseMap.error("上传失败");
                }
            }
            responseMap.put(ResponseMap.IMAGE_URL_LIST,imageUrlList);
        }catch(Exception e){
            e.printStackTrace();
        }
        return responseMap;
    }*/

    /**
     * oss通用图片批量上传
     * @param picture 大文本对象数组
     * @param ossFilePath 文件保存oss路径,不要域名
     * @return
     */
    /*public static ResponseMap upload(MultipartFile[] picture, String ossFilePath) {
        ResponseMap responseMap = ResponseMap.getInstance();
        List<String> imageUrlList = new ArrayList<>();
        try{
            for (MultipartFile pic : picture) {
                if (pic.getSize() > 0){
                    String originalFilename = pic.getOriginalFilename(); //获取图片原来名称
                    String filePathName = ossFilePath + CommonUtils.uuid() + "."+ FilenameUtils.getExtension(originalFilename); //真实的图片保存相对路径
                    boolean isOK = AliOssUtils.saveFileObject(filePathName,pic.getInputStream());//将图片上传到oss服务器
                    //boolean isOK = AliOssUtils.breakPointUpload(filePathName,pic);//将图片上传到oss服务器
                    if(isOK){
                        imageUrlList.add(filePathName);
                        responseMap.put(ResponseMap.MESSAGE,"上传成功");
                    }else{
                        return responseMap.error("上传失败");
                    }
                }
            }
            responseMap.put(ResponseMap.IMAGE_URL_LIST,imageUrlList);
        }catch(Exception e){
            e.printStackTrace();
        }
        return responseMap;
    }*/

    /**
     * 将本地文件上传到oss系统
     * @param localFilePath 本地文件目录路径
     * @param ossFilePath oss目录路径
     */
    /*public static ResponseMap upload(String localFilePath, String ossFilePath) {
        ResponseMap responseMap = ResponseMap.getInstance();
        try{
            File[] files = fileSort(localFilePath);
            List<String> imageUrlList = AliOssUtils.saveFileBatch(ossFilePath,files);//将图片上传到oss服务器
            System.out.println("图片上传oss完成");
            responseMap.put(ResponseMap.IMAGE_URL_LIST,imageUrlList);
        }catch(Exception e){
            e.printStackTrace();
            responseMap.error("上传失败");
        }
        return responseMap;
    }*/



    /**
     * 汪敏 2017-12-20
     * 服务器通用图片批量上传
     * @param picture 大文本对象数组
     * @param mp4TempDir 文件保存全路径
     */
    /*public static boolean uploadLocal(MultipartFile[] picture, String mp4TempDir) {
        boolean bool = false;
        try{
            for (int i = 0; i < picture.length; i++) {
                if (picture[i].getSize() > 0){
                    String originalFilename = picture[i].getOriginalFilename(); //获取图片原来名称
                    String filePathName = mp4TempDir + i + "."+ FilenameUtils.getExtension(originalFilename); //真实的图片保存相对路径
                    picture[i].transferTo(new File(filePathName));
                }
            }
            bool = true;
            System.out.println("图片上传到服务器完成");
        }catch(Exception e){
            e.printStackTrace();
        }
        return bool;
    }*/

    /**
     * 通过上一层目录和目录名得到最后的目录层次
     * @param previousDir 上一层目录
     * @param dirName 当前目录名
     */
    public static String getSaveDir(String previousDir, String dirName) {
        if (StringUtils.isNotBlank(previousDir)){
            dirName = previousDir + "/" + dirName + "/";
        }else {
            dirName = dirName + "/";
        }
        return dirName;
    }

    /**
     * 如果目录不存在，就创建文件
     * @return String
     */
    public static String mkdirs(String dirPath) {
        try{
            File file = new File(dirPath);
            if(!file.exists()){
                file.mkdirs();
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        return dirPath;
    }


    /**
     * 在Linux系统中读取文件时将文件排序
     * @param filePath
     * @return
     */
    public static File[] fileSort(String filePath){
        File[] files = new File(filePath).listFiles();
        assert files != null;
        int filesLength = files.length;
        String nextFix = FilenameUtils.getExtension(files[0].getName());
        File[] fileNames = new File[filesLength];
        for (int i = 0; i < filesLength; i++) {
            for (File file : files) {
                String absolutePath = file.getAbsolutePath();
                if (absolutePath.endsWith("/" + i + "." + nextFix) || absolutePath.endsWith("\\" + i + "." + nextFix)) {
                    fileNames[i] = new File(absolutePath);
                    break;
                }
            }
        }
        return fileNames;
    }


    /**
     * 普通文件下载，文件在服务器里面
     * @param request
     * @param response
     */
    public static void download(HttpServletRequest request, HttpServletResponse response) {
        try{
            //设置文件下载时，文件流的格式
            String realPath = request.getServletContext().getRealPath("/");
            realPath = realPath + "index.jsp";
            System.out.println("下载地址="+realPath);
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(realPath));
            BufferedOutputStream bos =  new BufferedOutputStream(response.getOutputStream());
            //下面这个变量保存的是要下载的文件拼接之后的完整路径
            String downName = realPath.substring(realPath.lastIndexOf("/") + 1);
            System.out.println("下载文件名="+downName);
            response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(downName,"utf-8"));
            byte[] buff = new byte[2048];
            int bytesRead;
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
            try {
                bis.close();
                bos.close();
            }catch (Exception e){
                e.printStackTrace();;
            }
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("下载出错");
        }
    }

    /**
     * 普通文件下载，文件路径固定
     * @param targetFile 下载的文件路径
     * @param response
     */
    public static void download(String targetFile, HttpServletResponse response) {
        try{
            System.out.println("下载文件路径="+targetFile);
            //设置文件下载时，文件流的格式
            InputStream bis = new BufferedInputStream(new FileInputStream(targetFile));
            OutputStream bos =  new BufferedOutputStream(response.getOutputStream());
            //下面这个变量保存的是要下载的文件拼接之后的完整路径
            String downName = targetFile.split("/")[1];
            System.out.println("下载文件名="+downName);
            response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode(downName,"utf-8"));
            byte[] buff = new byte[2048];
            int bytesRead;
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
            try {
                bis.close();
                bos.close();
            }catch (Exception e){
                e.printStackTrace();;
            }
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("下载出错");
        }
    }

    /**
     * 下载网络文件
     * @param targetFile
     * @param response
     */
    public static void downloadUrl(String targetFile, HttpServletResponse response) {
        try{
            URL website = new URL(targetFile);
            ReadableByteChannel rbc = Channels.newChannel(website.openStream());
            FileOutputStream fos = new FileOutputStream("D:/img/1.zip");//例如：test.txt
            fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
            fos.close();
        }catch(Exception e){
            e.printStackTrace();
            System.out.println("下载出错");
        }
    }

    /**
     * 删除文件
     * @param fileName
     * @return
     */
    public static boolean deleteFile (String fileName){
        try{
            File sourceFile = new File(fileName);
            if(sourceFile.isDirectory()){
                for (File listFile : Objects.requireNonNull(sourceFile.listFiles())) {
                    deleteFile(listFile.getAbsolutePath());
                }
            }
            return sourceFile.delete();
        }catch(Exception e){
            e.printStackTrace();
        }
        return false;
    }
}
